﻿using System;
using System.Runtime.Serialization;
using CC.Yacht.Interfaces;

namespace CC.Yacht
{
    [DataContract]
    public class Scorecard: IScorecard
    {
        #region Constructor
        public Scorecard()
        {
            Aces = -1;
            Twos = -1;
            Threes = -1;
            Fours = -1;
            Fives = -1;
            Sixes = -1;

            ThreeOfAKind = -1;
            FourOfAKind = -1;
            FullHouse = -1;
            SmallStraight = -1;
            LargeStraight = -1;
            Yacht = -1;
            Chance = -1;

            CalculateTotal();
        }

        public Scorecard(User owner) : this()
        {
            User = owner;
        }
        #endregion

        #region Public Events
        public event EventHandler<ScoreChangedEventArgs> Changed;
        #endregion

        #region Public Properties
        public int this[ScoreField scoreField]
        {
            get { return GetScoreField(scoreField); }
        }

        [DataMember]
        public int Aces { get; set; }

        [DataMember]
        public int Twos { get; set; }

        [DataMember]
        public int Threes { get; set; }

        [DataMember]
        public int Fours { get; set; }

        [DataMember]
        public int Fives { get; set; }

        [DataMember]
        public int Sixes { get; set; }

        [DataMember]
        public int UpperSectionSubtotal { get; set; }

        [DataMember]
        public int UpperSectionBonus { get; set; }

        [DataMember]
        public int UpperSectionTotal { get; set; }

        [DataMember]
        public int ThreeOfAKind { get; set; }

        [DataMember]
        public int FourOfAKind { get; set; }

        [DataMember]
        public int FullHouse { get; set; }

        [DataMember]
        public int SmallStraight { get; set; }

        [DataMember]
        public int LargeStraight { get; set; }

        [DataMember]
        public int Yacht { get; set; }

        [DataMember]
        public int Chance { get; set; }

        [DataMember]
        public int YachtBonuses { get; set; }

        [DataMember]
        public int LowerSectionTotal { get; set; }

        [DataMember]
        public int Total { get; set; }

        [DataMember]
        public User User { get; set; }
        #endregion

        #region Private Methods
        private void CalculateLowerSectionTotal()
        {
            LowerSectionTotal = 0;

            if (ThreeOfAKind > 0)
            {
                LowerSectionTotal += ThreeOfAKind;
            }

            if (FourOfAKind > 0)
            {
                LowerSectionTotal += FourOfAKind;
            }

            if (FullHouse > 0)
            {
                LowerSectionTotal += FullHouse;
            }

            if (SmallStraight > 0)
            {
                LowerSectionTotal += SmallStraight;
            }

            if (LargeStraight > 0)
            {
                LowerSectionTotal += LargeStraight;
            }
                                    
            if (Yacht > 0)
            {
                LowerSectionTotal += Yacht + (YachtBonuses * Constants.YachtBonusValue);
            }

            if (Chance > 0)
            {
                LowerSectionTotal += Chance;
            }
        }

        private void CalculateUpperSectionTotal()
        {
            UpperSectionSubtotal = 0;
            
            if (Aces > 0)
            {
                UpperSectionSubtotal += Aces;
            }

            if (Twos > 0)
            {
                UpperSectionSubtotal += Twos;
            }

            if (Threes > 0)
            {
                UpperSectionSubtotal += Threes;
            }

            if (Fours > 0)
            {
                UpperSectionSubtotal += Fours;
            }

            if (Fives > 0)
            {
                UpperSectionSubtotal += Fives;
            }
            
            if (Sixes > 0)
            {
                UpperSectionSubtotal += Sixes;
            }

            UpperSectionBonus = (UpperSectionSubtotal >= Constants.UpperSectionBonusMinScore) ? Constants.UpperSectionBonusValue : 0;
            UpperSectionTotal = UpperSectionBonus + UpperSectionSubtotal;
        }

        private void CalculateTotal()
        {
            CalculateLowerSectionTotal();
            CalculateUpperSectionTotal();
            Total = LowerSectionTotal + UpperSectionTotal;
        }
        #endregion

        #region Protected Methods
        protected void OnChanged(ScoreChangedEventArgs eventArgs)
        {
            if (Changed != null)
            {
                Changed(this, eventArgs);
            }
        }
        #endregion

        #region Public Methods
        public int GetScoreField(ScoreField scoreField)
        {
            int returnValue = -1;

            switch (scoreField)
            {
                case ScoreField.Aces:
                    {
                        returnValue = Aces;
                        break;
                    }
                case ScoreField.Chance:
                    {
                        returnValue = Chance;
                        break;
                    }
                case ScoreField.Fives:
                    {
                        returnValue = Fives;
                        break;
                    }
                case ScoreField.FourOfAKind:
                    {
                        returnValue = FourOfAKind;
                        break;
                    }
                case ScoreField.Fours:
                    {
                        returnValue = Fours;
                        break;
                    }
                case ScoreField.FullHouse:
                    {
                        returnValue = FullHouse;
                        break;
                    }
                case ScoreField.LargeStraight:
                    {
                        returnValue = LargeStraight;
                        break;
                    }
                case ScoreField.Sixes:
                    {
                        returnValue = Sixes;
                        break;
                    }
                case ScoreField.SmallStraight:
                    {
                        returnValue = SmallStraight;
                        break;
                    }
                case ScoreField.ThreeOfAKind:
                    {
                        returnValue = ThreeOfAKind;
                        break;
                    }
                case ScoreField.Threes:
                    {
                        returnValue = Threes;
                        break;
                    }
                case ScoreField.Twos:
                    {
                        returnValue = Twos;
                        break;
                    }
                case ScoreField.Yacht:
                    {
                        returnValue = Yacht;
                        break;
                    }
            }

            return returnValue;
        }

        public bool IsGameOver()
        {
            bool retVal = true;

            foreach (ScoreField scoreField in Enum.GetValues(typeof(ScoreField)))
            {
                if (!IsScoreFieldSet(scoreField))
                {
                    retVal = false;
                    break;
                }
            }

            return retVal;
        }

        public bool IsScoreFieldSet(ScoreField scoreField)
        {
            bool returnValue = false;

            switch (scoreField)
            {
                case ScoreField.Aces:
                    {
                        returnValue = (Aces > -1);
                        break;
                    }
                case ScoreField.Chance:
                    {
                        returnValue = (Chance > -1);
                        break;
                    }
                case ScoreField.Fives:
                    {
                        returnValue = (Fives > -1);
                        break;
                    }
                case ScoreField.FourOfAKind:
                    {
                        returnValue = (FourOfAKind > -1);
                        break;
                    }
                case ScoreField.Fours:
                    {
                        returnValue = (Fours > -1);
                        break;
                    }
                case ScoreField.FullHouse:
                    {
                        returnValue = (FullHouse > -1);
                        break;
                    }
                case ScoreField.LargeStraight:
                    {
                        returnValue = (LargeStraight > -1);
                        break;
                    }
                case ScoreField.Sixes:
                    {
                        returnValue = (Sixes > -1);
                        break;
                    }
                case ScoreField.SmallStraight:
                    {
                        returnValue = (SmallStraight > -1);
                        break;
                    }
                case ScoreField.ThreeOfAKind:
                    {
                        returnValue = (ThreeOfAKind > -1);
                        break;
                    }
                case ScoreField.Threes:
                    {
                        returnValue = (Threes > -1);
                        break;
                    }
                case ScoreField.Twos:
                    {
                        returnValue = (Twos > -1);
                        break;
                    }
                case ScoreField.Yacht:
                    {
                        returnValue = (Yacht > -1);
                        break;
                    }
            }

            return returnValue;
        }

        public void Reset()
        {
            Aces = -1;
            Twos = -1;
            Threes = -1;
            Fours = -1;
            Fives = -1;
            Sixes = -1;

            ThreeOfAKind = -1;
            FourOfAKind = -1;
            FullHouse = -1;
            SmallStraight = -1;
            LargeStraight = -1;
            Yacht = -1;
            Chance = -1;
            YachtBonuses = 0;

            CalculateTotal();
        }

        public bool SetScoreField(ScoreField scoreField, Dice dice)
        {
            bool returnValue = false;

            if (dice.HasRolled)
            {
                switch (scoreField)
                {
                    case ScoreField.Aces:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Aces = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Chance:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Chance = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Fives:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Fives = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.FourOfAKind:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                FourOfAKind = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Fours:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Fours = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.FullHouse:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                FullHouse = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.LargeStraight:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                LargeStraight = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Sixes:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Sixes = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.SmallStraight:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                SmallStraight = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.ThreeOfAKind:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                ThreeOfAKind = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Threes:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Threes = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Twos:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Twos = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            break;
                        }
                    case ScoreField.Yacht:
                        {
                            if (!IsScoreFieldSet(scoreField))
                            {
                                Yacht = dice.GetScore(scoreField);
                                returnValue = true;
                            }
                            else if (Yacht > 0 && dice.GetScore(scoreField) > 0)
                            {
                                YachtBonuses++;
                                returnValue = true;
                            }
                            break;
                        }
                }
            }

            if (returnValue)
            {
                CalculateTotal();
                OnChanged(new ScoreChangedEventArgs(scoreField));
            }

            return returnValue;
        }
        #endregion
    }
}
